Imports System.Reflection
Imports System.Runtime.InteropServices

Namespace Threads

    Friend Class TH_DataLogging
        Inherits Threads.MultiVersionThread

        Private m_nLogNumber As Integer = -1

        <StructLayout(LayoutKind.Sequential, Pack:=1)> _
        Private Structure _typLogSeek
            Dim nLogNo As UInt32   ' Log number
            Dim nSeqNo As UInt32   ' Number of entries
        End Structure

        <StructLayout(LayoutKind.Sequential, Pack:=1)> _
        Private Structure _typLogHdr
            Dim dts As UInt32
            Dim nLogNo As UInt32
            Dim nSeqNo As UInt32
            Dim nTicks As UInt32
            Dim nPktType As Byte
            Dim nPktSize As UInt16
        End Structure

        <StructLayout(LayoutKind.Sequential, Pack:=1)> _
        Private Structure _typLogSec
            Dim nPktType As Byte
            Dim nPktSize As UInt16
        End Structure

        <StructLayout(LayoutKind.Sequential, Pack:=1)> _
        Private Structure _typLogReport
            Dim dts As UInt32
            Dim nCnt As UInt32
            Dim nSector As UInt32
            Dim nSeqNo As UInt32
        End Structure

        Friend Event RecieveDatalog(ByVal sender As Object, ByVal e As DataLoggingEventArgs)

        Friend Sub New(ByVal handler As Comm.UsbCommunicationHandle, Optional ByVal nLogNumber As Integer = -1)
            MyBase.New("Downloading")

            m_UsbCommHandler = handler
            Me.m_nLogNumber = nLogNumber
        End Sub

        Private Sub NotifyDatalog(ByVal dataLog As DataStream.StreamLog)
            RaiseEvent RecieveDatalog(Me, New DataLoggingEventArgs(dataLog))
        End Sub

        Public Overrides Function HwV2Process() As Threads.MultiVersionThread.thread_result
            Return thread_result.rslt_board_unsupported
        End Function

        ''' <summary>
        ''' Converts a byte array into a defined structure of type specified
        ''' </summary>
        ''' <param name="bff">Byte array to convert</param>
        ''' <param name="Typ">Type to convert to </param>
        ''' <param name="offset">Offset within the byte array to begin from</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Private Function ToStruct(ByVal bff() As Byte, ByVal Typ As Type, Optional ByVal offset As Integer = 0) As Object
            If bff.Length < Marshal.SizeOf(Typ) + offset Then
                Throw New ArgumentException("Size of byte array with offset less than size required")
            End If

            Dim obj As Object = New Object
            Dim ptr As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(Typ))
            Marshal.Copy(bff, offset, ptr, Marshal.SizeOf(Typ))
            obj = Marshal.PtrToStructure(ptr, Typ)
            Marshal.FreeHGlobal(ptr)
            Return obj
        End Function
        ''' <summary>
        ''' Converts a structure into a byte array
        ''' </summary>
        ''' <param name="obj">Object to convert</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Private Function ToBytes(ByVal obj As Object) As Byte()
            Dim ptr As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(obj))
            Dim bff(Marshal.SizeOf(obj) - 1) As Byte
            Marshal.StructureToPtr(obj, ptr, True)
            Marshal.Copy(ptr, bff, 0, Marshal.SizeOf(obj))
            Return bff
        End Function
        ''' <summary>
        ''' Converts a structure into a  byte array and copies it into a buffer
        ''' </summary>
        ''' <param name="obj">Object to convert</param>
        ''' <param name="buffer">Buffer to copy into</param>
        ''' <param name="offset">Offset in buffer</param>
        ''' <remarks></remarks>
        Private Sub ToBytes(ByVal obj As Object, ByRef buffer() As Byte, ByVal offset As Integer)
            Dim arr() As Byte = ToBytes(obj)
            Array.Copy(arr, 0, buffer, offset, arr.Length)
        End Sub

        Private Function process() As Threads.MultiVersionThread.thread_result
            Dim frame As Comm.Comm_Frame = Nothing
            Dim chBuffer(511) As Byte
            Dim chTemp(511) As Byte
            Dim chData(20) As Byte
            Dim chConsult(512) As Byte
            Dim consult As New Consult(Me.m_UsbCommHandler)
            Dim bStatus As Boolean = True
            Dim report As _typLogReport
            Dim logs As New System.Collections.Generic.List(Of _typLogReport)

            Try
                Me.m_UsbCommHandler.Timeout = 8000

                ' Get report about all datalog's (count & size)
                frame = New Comm.Comm_Frame(Comm.Comm_Frame.BikiFrameType.ID_RUN_COMMAND, , Comm.Comm_Frame.BikiFrameAction.eCmdRunGetLogReport, , Comm.Comm_Frame.BikiFrameType.ID_GENERAL_SPECIFIC)
                If bStatus AndAlso m_UsbCommHandler.SendFrame(frame, chData, True) = Comm.Comm_Frame.BikiFrameResponse.CMD_OK AndAlso Not chData Is Nothing Then
                    report = ToStruct(chData, GetType(_typLogReport))
                Else
                    bStatus = False
                End If

                ' Get information about all datalog's interested in
                If bStatus Then
                    If Me.m_nLogNumber = -1 Then
                        Dim i As Integer = 1

                        ' For each data log, get the report on it
                        Do
                            Dim logReport As New _typLogReport

                            frame = New Comm.Comm_Frame(Comm.Comm_Frame.BikiFrameType.ID_RUN_COMMAND, i, Comm.Comm_Frame.BikiFrameAction.eCmdRunGetLogInfo, , Comm.Comm_Frame.BikiFrameType.ID_GENERAL_SPECIFIC)
                            If m_UsbCommHandler.SendFrame(frame, chData, True, 3) = Comm.Comm_Frame.BikiFrameResponse.CMD_OK AndAlso Not chData Is Nothing Then
                                logReport = ToStruct(chData, GetType(_typLogReport))
                                If logReport.nSeqNo > 3 Then
                                    logs.Add(logReport)
                                Else
                                    Debug.Print("datalog too small to download")
                                End If

                                Debug.Print("Log {0} Length {1} Sectpr {2}", i, logReport.nSeqNo, logReport.nSector)
                            Else
                                OnMessage(String.Format("Error retrieving infomartion on Datalog: {0}", i))
                                bStatus = False
                            End If

                            i += 1
                        Loop Until Not bStatus Or i >= report.nCnt

                    ElseIf Me.m_nLogNumber <= CInt(report.nCnt) Then
                        frame = New Comm.Comm_Frame(Comm.Comm_Frame.BikiFrameType.ID_RUN_COMMAND, Me.m_nLogNumber, Comm.Comm_Frame.BikiFrameAction.eCmdRunGetLogInfo, , Comm.Comm_Frame.BikiFrameType.ID_GENERAL_SPECIFIC)
                        If m_UsbCommHandler.SendFrame(frame, chData, True, 3) = Comm.Comm_Frame.BikiFrameResponse.CMD_OK Then
                            Dim logReport As New _typLogReport
                            logReport = ToStruct(chData, GetType(_typLogReport))
                            If logReport.nSeqNo > 3 Then
                                logs.Add(logReport)
                            Else
                                OnMessage(String.Format("Datalog too small to download: {0}", Me.m_nLogNumber))
                                bStatus = False
                            End If
                        Else
                            OnMessage(String.Format("Error retrieving infomartion on Datalog: {0}", Me.m_nLogNumber))
                            bStatus = False
                        End If

                    Else
                        OnMessage(String.Format("Datalog number:{0} does not exist, BikiSoft or Board fault", Me.m_nLogNumber))
                        bStatus = False
                    End If
                End If

                ' Calculate size of download
                If bStatus AndAlso logs.Count Then
                    ' for loop, each interested download
                    For Each log As _typLogReport In logs
                        ' Create a uniquie datetime
                        Dim dts As Date = Date.Parse("1/1/2008").AddSeconds(report.dts).AddMilliseconds(log.nCnt)
                        Dim streamLog As New DataStream.StreamLog()
                        Dim stream As DataStream.Stream
                        Dim logSeek As New _typLogSeek

                        Me.m_threadPurpose = log.nSeqNo

                        '   Seek to datalog
                        logSeek.nLogNo = log.nCnt
                        logSeek.nSeqNo = 0

                        '   Setup the streamlog
                        streamLog.LogName = String.Format("Log #{0}", log.nCnt)
                        ' Set the starttime to dts
                        streamLog.StartTime = dts

                        '   Download initial
                        frame = New Comm.Comm_Frame(Comm.Comm_Frame.BikiFrameType.ID_RUN_COMMAND, , Comm.Comm_Frame.BikiFrameAction.eCmdRunGetLogData, ToBytes(logSeek), Comm.Comm_Frame.BikiFrameType.eMsgIdLogData)
                        If m_UsbCommHandler.SendFrame(frame, chTemp, True, 2) = Comm.Comm_Frame.BikiFrameResponse.CMD_OK Then
                            '   Add to datalog
                            Dim logHdr As _typLogHdr = ToStruct(chTemp, GetType(_typLogHdr))
                            '   copy to a temp array for importing
                            Array.Copy(chTemp, Marshal.SizeOf(GetType(_typLogHdr)), chConsult, 7, logHdr.nPktSize)
                            '   Add to the existing datalog
                            stream = consult.decodeStream(chConsult)
                            stream.setTimeStamp(dts.AddMilliseconds(logHdr.nTicks))
                            streamLog.Add(stream)

                            '   Download all compressed data
                            For i As Integer = 1 To log.nSeqNo - 1

                                ' Exit if canceled
                                If Me.bgw.CancellationPending Then
                                    Return thread_result.rslt_canceled
                                ElseIf Not bStatus Then
                                    Return thread_result.rslt_comms_error
                                End If

                                logSeek.nSeqNo = i
                                frame = New Comm.Comm_Frame(Comm.Comm_Frame.BikiFrameType.ID_RUN_COMMAND, , Comm.Comm_Frame.BikiFrameAction.eCmdRunGetLogData, ToBytes(logSeek), Comm.Comm_Frame.BikiFrameType.eMsgIdLogData)
                                If m_UsbCommHandler.SendFrame(frame, chTemp, True, 2) = Comm.Comm_Frame.BikiFrameResponse.CMD_OK Then
                                    logHdr = ToStruct(chTemp, GetType(_typLogHdr))
                                    Array.Copy(chTemp, Marshal.SizeOf(GetType(_typLogHdr)), chConsult, 7, logHdr.nPktSize)
                                    stream = consult.decodeStream(chConsult)
                                    stream.setTimeStamp(dts.AddMilliseconds(logHdr.nTicks))
                                    streamLog.Add(stream)

                                    OnProgress(i)
                                    '       Decompress
                                    '       Add to datalog
                                Else
                                    bStatus = False
                                End If
                            Next

                            '   Notify datalog via event
                            NotifyDatalog(streamLog)

                            ' end loop
                        Else
                            Return thread_result.rslt_comms_error
                        End If
                    Next

                End If

            Catch ex As USB_exceptions
                Return thread_result.rslt_comms_error
            Catch ex As Comm_Exceptions
                Return thread_result.rslt_comms_error
            End Try
        End Function

        Public Overrides Function HwV3Process() As Threads.MultiVersionThread.thread_result
            Dim result As Threads.MultiVersionThread.thread_result
            Dim frame As Comm.Comm_Frame = Nothing

            Try
                frame = New Comm.Comm_Frame(Comm.Comm_Frame.BikiFrameType.ID_RUN_COMMAND, 0, Comm.Comm_Frame.BikiFrameAction.eCmdRunDataLogging, , Comm.Comm_Frame.BikiFrameType.ID_LAST_COMMAND_STATUS)
                If m_UsbCommHandler.SendFrame(frame, Nothing, True) = Comm.Comm_Frame.BikiFrameResponse.CMD_OK Then
                    Threading.Thread.Sleep(100)

                    result = process()

                    frame = New Comm.Comm_Frame(Comm.Comm_Frame.BikiFrameType.ID_RUN_COMMAND, 1, Comm.Comm_Frame.BikiFrameAction.eCmdRunDataLogging, , Comm.Comm_Frame.BikiFrameType.ID_LAST_COMMAND_STATUS)
                    If Not m_UsbCommHandler.SendFrame(frame, Nothing, True) = Comm.Comm_Frame.BikiFrameResponse.CMD_OK Then
                        OnMessage("Unable to restart datalogging")
                        result = thread_result.rslt_board_fault
                    End If

                Else
                    result = thread_result.rslt_board_fault
                    OnMessage("Unable to stop datalogging")
                End If

            Catch ex As USB_exceptions

            Catch ex As Comm_Exceptions

            Catch ex As Exception

            End Try

            Return result
        End Function

        Public Overrides Function HwV4Process() As Threads.MultiVersionThread.thread_result
            Return thread_result.rslt_board_unsupported
        End Function
    End Class

    ''' <summary>
    ''' DataLogging event to pass a complete datalog back to the main application
    ''' </summary>
    ''' <remarks></remarks>
    Friend Class DataLoggingEventArgs
        Inherits System.EventArgs

        Private m_DataLog As DataStream.StreamLog = Nothing

        Sub New(ByVal DataLog As DataStream.StreamLog)
            m_DataLog = DataLog
        End Sub

        ''' <summary>
        ''' Retrieves the datalog from the event
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property DataLog() As DataStream.StreamLog
            Get
                Return m_DataLog
            End Get
        End Property

    End Class
End Namespace